#!/usr/bin/env python
# -*- coding:utf-8 -*-


import re
import md5

from urlparse import urlparse

class   MapPage(object) :

    def __init__(self) :
        self.map_max = 1
        self.map_rules = []
        self.map_largesites = {}
        self.map_version = 'v1'
        self.map_services = []
        self.map_peers = {}

        self.opened = False
        # __init__()

    def load( self, filename ) :
        #group20@221.194.137.54:4705
        __pattern = re.compile( r'\s*(\w+)\@([^:]+):(\d+)\s*' )
        for line in file(filename) :
            line = line.strip()
            if len(line) < 2 : continue
            if line[0] == '#' : continue
            if line[0] == '[' : continue
            key, value = line.split( '=' )
            key = key.strip()
            value = value.strip()
            if 'map.max' == key :
                self.map_max = int( value )
                continue
            if 'map.version' == key :
                self.map_version = value
                continue
            if 'map.services' == key :
                self.map_services = value.split()
                continue
            if key.startswith('map.rule.') :
                # format: map.rule.group01 = 1 - 1234
                _m, _r, _group = key.split('.', 2)
                _min, _max = value.split('-', 1)
                _min = int(_min)
                _max = int(_max)
                while len(self.map_rules) < _min :
                    self.map_rules.append( None )
                for i in range( _min, _max + 1 ) :
                    if i < len(self.map_rules) :
                        self.map_rules[ i ] = _group
                    else :
                        self.map_rules.append( _group )
                continue

            if key.startswith('map.service.') :
                # format: map.service.group01 = service01@host:port service01@host:port
                _m, _s, _group = key.split('.', 2)
                self.map_peers[ _group ] = value.split()
                continue

            if 'map.largesite' == key :
                ##self.map_largesites.append( value )
                hostname = value
                self.map_largesites[ hostname ] = self.hash_hostname( hostname )
                continue

            print 'unhandle:',line

        return  True
        # load()

    def open(self, configure) :
        if configure is None : return

        self.load(configure)

        self.opened = True
        # open()

    def load_with_configparser(self, configure) :
        from ConfigParser import ConfigParser

        config = ConfigParser()
        config.readfp(open(configure))

        SECTION_BASIC = 'map.basic'
        map_max = config.getint(SECTION_BASIC, 'map.max')
        if map_max < 1 :
            raise RuntimeError, '[%s] invalid map.max=%d' % (SECTION_BASIC, map_max)
        self.map_max = map_max
        if config.has_option(SECTION_BASIC, 'map.version') :
            self.map_version = config.get(SECTION_BASIC, 'map.version')
        if config.has_option(SECTION_BASIC, 'map.services') :
            services = config.get(SECTION_BASIC, 'map.services')
            self.map_services = services.split()

        SECTION_RULE = 'map.rule'
        items = config.items(SECTION_RULE)
        for name,value in items :
            if name.startswith('map.rule.') :
                # format: map.rule.group01 = 1 - 1234
                _m, _r, _group = name.split('.', 2)
                _min, _max = value.split('-', 1)
                _min = int(_min)
                _max = int(_max)
                while len(self.map_rules) < _min :
                    self.map_rules.append( None )
                for x in xrange(_min, _max + 1) :
                    self.map_rules.append( _group )
                print '%s [%d .. %d]' % (_group, _min, _max)
            else :
                pass
        #print self.map_rules

        # skip this section, now
        SECTION_SERVICE = 'map.service'
        items = config.items(SECTION_SERVICE)
        for name,value in items :
            if name.startswith('map.service.') :
                # format: map.service.group01 = service01@host:port service01@host:port
                _m, _s, _group = name.split('.', 2)
                self.map_peers[ _group ] = value.split()

        SECTION_LARGE_SITE = 'map.largesite'
        items = config.items(SECTION_LARGE_SITE)
        for name,value in items :
            if name.startswith('map.largesite') :
                _m, _l, _site = name.split('.', 2)
                self.map_largesites.append( _site )


    def close(self) :
        if not self.opened : return

        self.opened = False
        # close()

    def map(self, service, key) :
        '''
        response '[service:<service> \t] status:<code> \t key:<key> \t value:<value>'
        '''
        if not self.opened :
            r = []
            if service : r.append('service:%s' % service)
            r.append('status:501')
            r.append('error:server is not ready, yet.')
            r.append('key:%s' % key)
            result = '\t' .join(r)
            return result
        if service and (service not in self.map_services) :
            r = []
            r.append('service:%s' % service)
            r.append('status:404')
            r.append('error:unknown service name "%s"' % service)
            r.append('key:%s' % key)
            result = '\t' .join(r)
            return result

        status,error,cid,version,group,peers = self.doMap(key)

        r = []
        if service : r.append('service:%s' % service)
        r.append('status:%d' % status)
        r.append('key:%s' % key)
        if error :
            r.append('error:%s' % error)
        else :
            r.append('cid:%d' % cid)
            r.append('version:%s' % version)
            r.append('group:%s' % group)
            for _p in peers :
                r.append('peer:%s' % _p)
        result = '\t' .join(r)
        return result
        # map()

    def doMap(self, url) :
        '''
        return three-tuple( status, error, value )
        '''
        if url is None : return 502,'empty url', None
        url = url.strip()
        if len(url) < 2 : return 503, 'invalid url "%s"' % url, None

        __f = urlparse( url )
        hostname = __f.hostname
        if hostname is None :
            hash = self.hash_hostname( url )
        else :
            ##hash = self.hash_hostname( hostname )
            ##if hostname in self.map_largesites :
            ##    hash += self.hash_url( url )
            if hostname in self.map_largesites :
                hash = self.map_largesites[ hostname ] + self.hash_url( url )
            else :
                hash = self.hash_hostname( hostname )

        hash = (hash % self.map_max) + 1

        group = self.map_rules[hash]
        peers = self.map_peers[group]
        return 200,None,hash, self.map_version, group, peers
        # doMap()

    def hash_url( self, url ) :
        m = md5.new()
        m.update( url )
        hash = int(m.hexdigest()[:1], 16)

        return  hash

    def hash_hostname( self, hostname ) :
        m = md5.new()
        m.update( hostname )
        hash = int(m.hexdigest()[:3], 16)
        hash = hash << 4

        return  hash

    # class MapPage
